#define STRICT
#define ORBITER_MODULE

#include "D3D11Client.h"
#include "VideoTab.h"
#include "Scene.h"
#include "Texture.h"
#include "TextureMgr.h"
#include "D3D11Pad.h"
#include "Version.h"

HINSTANCE hDLL;
// TODO: Do we need this, used in Init/ExitModule() ?
D3D11Client *_gcl;

// Single global client object
D3D11Client *gGraphicsClient = NULL;

// Created here and also used in VideoTab.h
VideoTab *gVideoTab = NULL;

#define APP_TITLE "Test - built on "__DATE__

//#pragma region Initialization
D3D11Client::D3D11Client( HINSTANCE hIn ) : oapi::GraphicsClient( hIn ) {
    oapiWriteLogV("D3D11Client::D3D11Client");

    // Initialize globals in Resources.h
    PCounter = new PerformanceCounter();
    mConfig = new D3D11Config(this);

    mbDisplayHUD = true;

    mhWindow = NULL;
    mbVideoTab = mbEnumerate = mbPopup = false;

    mbSplashScreenInited = false;
}

D3D11Client::~D3D11Client() {
    oapiWriteLogV("D3D11Client::~D3D11Client");
	mShaderManager.reset();
    iCtx->ClearState();
    iCtx->Flush();
    REL( iCtx );
    REL( gd3dDevice );
    delete PCounter;
    delete mConfig;
    if( mbVideoTab )
        delete gVideoTab;
}

bool D3D11Client::clbkInitialise() {
    oapiWriteLogV("D3D11Client::clbkInitialise");
    if( !oapi::GraphicsClient::clbkInitialise() )        return false;
    auto vdata = gGraphicsClient->GetVideoData();
    return true;
}

bool D3D11Client::clbkUseLaunchpadVideoTab() const {
    oapiWriteLogV("D3D11Client::clbkUseLaunchpadVideoTab");
    return true;
}

HWND D3D11Client::clbkCreateRenderWindow() {
    oapiWriteLogV("D3D11Client::clbkCreateRenderWindow");
    WLOG2( APP_TITLE" is starting up..." );


    auto data = GetVideoData();
    mhWindow = oapi::GraphicsClient::clbkCreateRenderWindow();
    /*auto currStyle = GetWindowLong(mhWindow, GWL_STYLE);
    auto currExStyle = GetWindowLong(mhWindow, GWL_EXSTYLE);
    auto style = WS_POPUP;
    auto exStyle = WS_EX_TOPMOST;*/
    SetWindowTextA( mhWindow, "- "APP_TITLE );

    EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &mCurrentMode);
    if (!data->fullscreen)
    {
        WINDOWINFO winfo;
        winfo.cbSize = sizeof( WINDOWINFO );
        GetWindowInfo( mhWindow, &winfo );
        mConfig->cWidth = abs( winfo.rcClient.right - winfo.rcClient.left );
        mConfig->cHeight = abs( winfo.rcClient.bottom - winfo.rcClient.top );
    }
    else
    {
        mConfig->cWidth = data->winw;
        mConfig->cHeight = data->winh;
        auto currExStyle = GetWindowLong(mhWindow, GWL_EXSTYLE);
        currExStyle |= WS_EX_TOPMOST;
        SetWindowLong(mhWindow, GWL_EXSTYLE, currExStyle);
        if (mCurrentMode.dmPelsWidth != mConfig->cWidth || mCurrentMode.dmPelsHeight != mConfig->cHeight)
        {
            DEVMODE mode;
            CopyMemory(&mode, &mCurrentMode, sizeof(DEVMODE));
            mode.dmBitsPerPel = 32;
            mode.dmPelsWidth = mConfig->cWidth;
            mode.dmPelsHeight = mConfig->cHeight;
            mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
            ChangeDisplaySettings(&mode, 0);
            mModeChanged = true;
        }
        MoveWindow(mhWindow, 0, 0, mConfig->cWidth, mConfig->cHeight, true);
    }
    mConfig->Aspect = (float)mConfig->cWidth/(float)mConfig->cHeight;

    if( !mbEnumerate ) {
    //    Device enumeration always fails when placed before clbkCreateRenderWindow).
        mConfig->EnumerateAll( GetVideoData() );
        mConfig->LoadConfig();
        mConfig->ApplyConfig();
        mbEnumerate = true;
    }

    mConfig->ApplyConfig();
    mScene = new Scene( this );

    InitShaderProfiles(gd3dDevice->GetFeatureLevel());
	mShaderManager.reset(new ShaderManager(gd3dDevice));

    mScene->Init3D();

    mTexMgr = new TextureMgr(this);
    mTexMgr->GInit();
    mScene->InitStatics();
    InitSplashScreen();

    ProgressString( "write something here", 2 );
    ProgressString( "write something here 2", 0 );

    return mhWindow;
}


void D3D11Client::clbkUpdate( bool running ) {
    oapiWriteLogV("D3D11Client::clbkUpdate");
    //everything is updated in clbkRenderScene() while rendering thread is stopped
}

void D3D11Client::clbkRenderScene() {
    oapiWriteLogV("D3D11Client::clbkRenderScene");
    static const LONG WStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
    static DWORD nPopup, j;
    static const HWND *PopupList;
    auto data = GetVideoData();
//popup windows: ensure they all have close button.
    if( mbPopup ) {
        nPopup = GetPopupList( &PopupList );
        if( nPopup && GetWindowLong( PopupList[j], GWL_STYLE ) != WStyle )
            for( j = 0; j < nPopup; j++ )
                SetWindowLong( PopupList[j], GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE );//add close button to all popup windows
        else
        {
            mbPopup = false;
            if (data->fullscreen)
            {
                SwapChain->SetFullscreenState(TRUE, nullptr);
            }
        }
    }

    //scene rendering.

    if( mbSplashScreenInited )    ExitSplashScreen();

    mScene->SaveParams();
    mScene->Update();

    mScene->Render_0();
    mScene->Render_1();

    SwapChain->Present( data->novsync ? 0 : 1, 0 );

}

bool D3D11Client::clbkDisplayFrame() {
    oapiWriteLogV("D3D11Client::clbkDisplayFrame");
    return true;
}

void D3D11Client::clbkRender2DPanel( SURFHANDLE *srf, MESHHANDLE hMesh, MATRIX3 *T, bool transparent ) {
    oapiWriteLogV("D3D11Client::clbkRender2DPanel");
}

void D3D11Client::WaitForFrameSync() {
/*
    Rare events. (new vessel, delete vessel etc.) Main thread waits for rendering thread's "FrameReady" message.
    though, that can be done by the same way, used for GDI...
*/
    oapiWriteLogV("D3D11Client::WaitForFrameSync");

}

void D3D11Client::ToggleHUD()
{
    oapiWriteLogV("D3D11Client::ToggleHUD");
    mbDisplayHUD = !mbDisplayHUD;
}

LRESULT D3D11Client::RenderWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) {
    oapiWriteLogV("D3D11Client::RenderWndProc");
    if( msg == WM_KEYDOWN )
    {
        if (wp == VK_HOME && GetKeyState(VK_CONTROL))
        {
            gGraphicsClient->ToggleHUD();
        }
    }

    return oapi::GraphicsClient::RenderWndProc (hwnd, msg, wp, lp);
}

void D3D11Client::clbkPostCreation() {
    WLOG2( "D3D11Client::clbkPostCreation" );
    mScene->InitObjects();
}

void D3D11Client::clbkPreOpenPopup() {
    oapiWriteLogV("D3D11Client::clbkPreOpenPopup");
    mbPopup = true;
    auto data = GetVideoData();
    if (data->fullscreen)
    {
        SwapChain->SetFullscreenState(FALSE, nullptr);
    }
}

void D3D11Client::clbkGetViewportSize( DWORD *w, DWORD *h ) const {
    WLOG2( "D3D11Client::clbkGetViewportSize" );
    *w = mConfig->cWidth;
    *h = mConfig->cHeight;
}

bool D3D11Client::clbkFullscreenMode() const {
    WLOG2( "D3D11Client::clbkFullscreenMode" );
    return mConfig->FullScreenWindow;
}

bool D3D11Client::clbkGetRenderParam( DWORD param, DWORD *value ) const {
    oapiWriteLogV("D3D11Client::clbkGetRenderParam");
    switch( param ) {
        case RP_COLOURDEPTH:
            *value = 32;
            return true;
        case RP_ZBUFFERDEPTH:
            *value = 24;
            return true;
        case RP_STENCILDEPTH:
            *value = 8;
            return true;
        case RP_MAXLIGHTS:
            *value = 12;
            return true;
        case RP_ISTLDEVICE:
            *value = TRUE;
            return true;
        case RP_REQUIRETEXPOW2:
            *value = FALSE;
            return true;
    }
    return false;
}

//LAUNCHPAD.
BOOL D3D11Client::LaunchpadVideoWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) {
    oapiWriteLogV("D3D11Client::LaunchpadVideoWndProc");
    if( !mbVideoTab ) {
        if( !mbEnumerate ) {
            mConfig->EnumerateAll( GetVideoData() );
            mConfig->LoadConfig();
            mConfig->ApplyConfig();
            mbEnumerate = true;
        }
        mbVideoTab = true;
        gVideoTab = new VideoTab( this, oapi::GraphicsClient::LaunchpadVideoTab(), hDLL );
        gVideoTab->InitTextBoxes();
    }
    if (gVideoTab)
    {
        return gVideoTab->LaunchpadVideoWndProc( hwnd, msg, wp, lp );
    }
    else
    {
        return oapi::GraphicsClient::LaunchpadVideoWndProc(hwnd, msg, wp, lp);
    }
}

void D3D11Client::clbkRefreshVideoData() {
    oapiWriteLogV("D3D11Client::clbkRefreshVideoData");
    if( gVideoTab )
        gVideoTab->UpdateConfig();
}

void D3D11Client::clbkCloseSession( bool fastclose ) {
    WLOG2( "D3D11Client::clbkCloseSession" );
    oapi::GraphicsClient::clbkCloseSession( fastclose );
}

void D3D11Client::clbkDestroyRenderWindow( bool fastclose ) {
    WLOG2( "D3D11Client::clbkDestroyRenderWindow" );
//terminate rendering thread.
    if (mModeChanged)
    {
        ChangeDisplaySettings(&mCurrentMode, 0);
    }
    mScene->Exit3D();
//cleaning up scene.
    delete mScene;
    oapi::GraphicsClient::clbkDestroyRenderWindow( fastclose );
}

#pragma region Mesh and Object Functions
//vessels.
int D3D11Client::clbkVisEvent( OBJHANDLE hObj, VISHANDLE vis, DWORD msg, UINT context ) {
    oapiWriteLogV("D3D11Client::clbkVisEvent");
    return 0;
}

void D3D11Client::clbkNewVessel( OBJHANDLE obj ) {
    oapiWriteLogV("D3D11Client::clbkNewVessel");
}

void D3D11Client::clbkDeleteVessel( OBJHANDLE hVessel ) {
    oapiWriteLogV("D3D11Client::clbkDeleteVessel");
}

MESHHANDLE D3D11Client::clbkGetMesh( VISHANDLE vis, UINT idx ) {
    oapiWriteLogV("D3D11Client::clbkGetMesh");
    return NULL;
}

int D3D11Client::clbkEditMeshGroup( DEVMESHHANDLE hMesh, DWORD grpidx, GROUPEDITSPEC *ges ) {
    oapiWriteLogV("D3D11Client::clbkEditMeshGroup");
    return 0;
}

bool D3D11Client::clbkSetMeshTexture( DEVMESHHANDLE hMesh, DWORD texidx, SURFHANDLE tex ) {
    oapiWriteLogV("D3D11Client::clbkSetMeshTexture");
	return true;
}

int D3D11Client::clbkSetMeshMaterial( DEVMESHHANDLE hMesh, DWORD matidx, const MATERIAL *mat ) {
    oapiWriteLogV("D3D11Client::clbkSetMeshMaterial");
    return 0;
}

bool D3D11Client::clbkSetMeshProperty( DEVMESHHANDLE hMesh, DWORD prop, DWORD value ) {
    oapiWriteLogV("D3D11Client::clbkSetMeshProperty");
    return 0;
}

void D3D11Client::clbkStoreMeshPersistent( MESHHANDLE hMesh, const char *fname ) {
    //no need to sync.
    oapiWriteLogV("D3D11Client::clbkStoreMeshPersistent");
    oapiWriteLogV("MESH loaded from file : %s", fname);
}
#pragma endregion
#pragma region TextureFunctions
SURFHANDLE D3D11Client::clbkLoadTexture( const char *fname, DWORD flags ) {
    oapiWriteLogV("D3D11Client::clbkLoadTexture");
    return (SURFHANDLE)mTexMgr->LoadTextureFromFile( fname, flags );
}

SURFHANDLE D3D11Client::clbkLoadSurface (const char *fname, DWORD attrib)
{
    oapiWriteLogV("D3D11Client::clbkLoadSurface");
    return (SURFHANDLE)mTexMgr->LoadTextureFromFile( fname, 0 );
}

SURFHANDLE D3D11Client::clbkCreateTexture( DWORD w, DWORD h ) {
    oapiWriteLogV("D3D11Client::clbkCreateTexture");
    return (SURFHANDLE)mTexMgr->CreateSurface( w, h );
}

void D3D11Client::clbkReleaseTexture( SURFHANDLE tex ) {
    oapiWriteLogV("D3D11Client::clbkReleaseTexture");
    mTexMgr->ReleaseTexture( (Texture*)tex );
}

HDC D3D11Client::clbkGetSurfaceDC( SURFHANDLE surf ) {
    oapiWriteLogV("D3D11Client::clbkGetSurfaceDC");
    return mTexMgr->GetSurfaceDC( (Texture*)surf );
}

void D3D11Client::clbkReleaseSurfaceDC( SURFHANDLE surf, HDC hdc ) {
    oapiWriteLogV("D3D11Client::clbkReleaseSurfaceDC");
    mTexMgr->ReleaseSurfaceDC( hdc, (Texture*)surf );
}

bool D3D11Client::clbkReleaseSurface( SURFHANDLE surf ) {
    oapiWriteLogV("D3D11Client::clbkReleaseSurface");
    mTexMgr->ReleaseTexture( (Texture*)surf );
    return true;
}

SURFHANDLE D3D11Client::clbkCreateSurface( DWORD w, DWORD h, SURFHANDLE hTempl ) {
    oapiWriteLogV("D3D11Client::clbkCreateSurface");
    return (SURFHANDLE)mTexMgr->CreateSurface( w, h );
}

SURFHANDLE D3D11Client::clbkCreateSurfaceEx (DWORD w, DWORD h, DWORD attrib)
{
    oapiWriteLogV("D3D11Client::clbkCreateSurfaceEx");
    return (SURFHANDLE)mTexMgr->CreateSurfaceEx(w, h, attrib);
}

SURFHANDLE D3D11Client::clbkCreateSurface( HBITMAP hbm ) {
    oapiWriteLogV("D3D11Client::clbkCreateSurface");
    return (SURFHANDLE)mTexMgr->CreateSurfaceBitmap( hbm );
}

bool D3D11Client::clbkGetSurfaceSize( SURFHANDLE surf, DWORD *w, DWORD *h ) {
    oapiWriteLogV("D3D11Client::clbkGetSurfaceSize");
    mTexMgr->GetSize( (Texture*)surf, w, h );
    return true;
}

bool D3D11Client::clbkCopyBitmap( SURFHANDLE pdds, HBITMAP hbm, int x, int y, int dx, int dy ) {
    oapiWriteLogV("D3D11Client::clbkCopyBitmap");
    return mTexMgr->CopyBitmap( (Texture*)pdds, hbm, x, y, dx, dy );
}

bool D3D11Client::clbkBlt( SURFHANDLE tgt, DWORD tgtx, DWORD tgty, SURFHANDLE src, DWORD flags ) const {
    oapiWriteLogV("D3D11Client::clbkBlt 1");
    return mTexMgr->Blit1( (Texture*)tgt, tgtx, tgty, (Texture*)src, flags );
}

bool D3D11Client::clbkBlt( SURFHANDLE tgt, DWORD tgtx, DWORD tgty, SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD w, DWORD h, DWORD flags ) const {
    oapiWriteLogV("D3D11Client::clbkBlt 2");
    return mTexMgr->Blit2( (Texture*)tgt, tgtx, tgty,  (Texture*)src, srcx, srcy, w, h, flags );
}

bool D3D11Client::clbkScaleBlt( SURFHANDLE tgt, DWORD tgtx, DWORD tgty, DWORD tgtw, DWORD tgth, SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD srcw, DWORD srch, DWORD flags ) const {
    oapiWriteLogV("D3D11Client::clbkScaleBlt");
    return mTexMgr->ScaleBlit( (Texture*)tgt, tgtx, tgty, tgtw, tgth, (Texture*)src, srcx, srcy, srcw, srch, flags );
}

bool D3D11Client::clbkFillSurface( SURFHANDLE surf, DWORD col ) const {
    oapiWriteLogV("D3D11Client::clbkFillSurface 1");
    return mTexMgr->FillSurface( (Texture*)surf, col );
}

bool D3D11Client::clbkFillSurface( SURFHANDLE surf, DWORD tgtx, DWORD tgty, DWORD w, DWORD h, DWORD col ) const {
    oapiWriteLogV("D3D11Client::clbkFillSurface 2");
    return mTexMgr->FillSurfaceRect( (Texture*)surf, tgtx, tgty, w, h, col );
}

DWORD D3D11Client::clbkGetDeviceColour( BYTE r, BYTE g, BYTE b ) {
    oapiWriteLogV("D3D11Client::clbkGetDeviceColour");
    return mTexMgr->GetDevColor( r, g, b );
}

bool D3D11Client::clbkSetSurfaceColourKey( SURFHANDLE surf, DWORD ckey ) {
    oapiWriteLogV("D3D11Client::clbkSetSurfaceColourKey");
    if( surf )
        return mTexMgr->SetColorKey( (Texture*)surf, ckey );
    return false;
}

void D3D11Client::clbkIncrSurfaceRef( SURFHANDLE surf ) {
    oapiWriteLogV("D3D11Client::clbkIncrSurfaceRef");
    mTexMgr->IncrSurfaceRef( (Texture*)surf );
}
#pragma endregion surface and texture functions
#pragma region Sketchpad/Fonts/Brushes/Pens
//GDI.
oapi::Sketchpad *D3D11Client::clbkGetSketchpad( SURFHANDLE surf ) {
//    Creates one of 2 possible versions of 2D Sketchpad
    oapiWriteLogV("D3D11Client::clbkGetSketchpad");
    HDC hDC;
    Texture *tex = (Texture*)surf;
    if( !tex )
    	mScene->BBDrawCount++;

    if( mConfig->SketchPadMode > 1 ) {        //D3D11Pad for back buffer and surfaces both    [mode 2]
        if( !mTexMgr->Find( tex ) )
            return NULL;

        if( !tex ) {
            mScene->PBuffers_counter++;
            return new D3D11Pad(mConfig, mScene, surf, mTexMgr );
        }
        else {
            if( tex->IsTex() )
                mTexMgr->ConvertTextureToSurface( tex );

			D3D11Pad *t = new D3D11Pad(mConfig, mScene, surf, mTexMgr);
            return t;
        }
    }
    return NULL;
}

void D3D11Client::clbkReleaseSketchpad( oapi::Sketchpad *Skpad ) {
    oapiWriteLogV("D3D11Client::clbkReleaseSketchpad");
    if( !Skpad )
        return;

    GDIPad *gdi_pad;
    D3D11Pad *d3d11_pad;

    if( mConfig->SketchPadMode > 1 ) {            //D3D11Pad for back buffer and surfaces both    [mode 2]
        d3d11_pad = (D3D11Pad*)Skpad;
        delete d3d11_pad;
        return;
    }
}

oapi::Font *D3D11Client::clbkCreateFont( int height, bool prop, const char *face, oapi::Font::Style style, int orientation ) const {
    oapiWriteLogV("D3D11Client::clbkCreateFont");
    return new D3D11PadFont( height, prop, face, style, orientation );
}

void D3D11Client::clbkReleaseFont( oapi::Font *font ) const {
    oapiWriteLogV("D3D11Client::clbkReleaseFont");
    delete ((D3D11PadFont*)font);
}

oapi::Pen *D3D11Client::clbkCreatePen( int style, int width, DWORD col ) const {
    oapiWriteLogV("D3D11Client::clbkCreatePen");
    return new D3D11PadPen( style, width, col );
}

void D3D11Client::clbkReleasePen( oapi::Pen *pen ) const {
    oapiWriteLogV("D3D11Client::clbkReleasePen");
    delete ((D3D11PadPen*)pen);
}

oapi::Brush *D3D11Client::clbkCreateBrush( DWORD col ) const {
    oapiWriteLogV("D3D11Client::clbkCreateBrush");
    return new D3D11PadBrush( col );
}

void D3D11Client::clbkReleaseBrush( oapi::Brush *brush ) const {
    oapiWriteLogV("D3D11Client::clbkReleaseBrush");
    delete ((D3D11PadBrush*)brush);
}

#pragma endregion GDI drawing functions
#pragma region PStreamFunctions
oapi::ParticleStream *D3D11Client::clbkCreateExhaustStream( PARTICLESTREAMSPEC *pss, OBJHANDLE obj, const double *lvl, const VECTOR3 *ref, const VECTOR3 *dir ) {
    oapiWriteLogV("D3D11Client::clbkCreateExhaustStream 1");
	return NULL;   
}

oapi::ParticleStream *D3D11Client::clbkCreateExhaustStream( PARTICLESTREAMSPEC *pss, OBJHANDLE obj, const double *lvl, const VECTOR3 &ref, const VECTOR3 &dir ) {
    oapiWriteLogV("D3D11Client::clbkCreateExhaustStream 2");
	return NULL;
}

oapi::ParticleStream *D3D11Client::clbkCreateReentryStream( PARTICLESTREAMSPEC *pss, OBJHANDLE obj ) {
    oapiWriteLogV("D3D11Client::clbkCreateReentryStream");
	return NULL;
}

bool D3D11Client::clbkParticleStreamExists( const oapi::ParticleStream *ps ) {
    oapiWriteLogV("D3D11Client::clbkParticleStreamExists");
    return false;
}
#pragma endregion Particle stream functions (temporarily void)

oapi::ScreenAnnotation *D3D11Client::clbkCreateAnnotation() {
    oapiWriteLogV("D3D11Client::clbkCreateAnnotation");
    return oapi::GraphicsClient::clbkCreateAnnotation();
}

//module functions.
DLLCLBK void InitModule( HINSTANCE h )
{
    oapiWriteLogV("D3D11Client::InitModule");
    HMODULE hd3d11dll = ::LoadLibrary("d3d11.dll");
    if (!hd3d11dll)
    {
        ::MessageBox(0, "This module requires DirectX 11 to be installed.", "DirectX 11 is not installed", MB_OK | MB_ICONERROR);
        return;
    }
    ::FreeLibrary(hd3d11dll);
    hDLL = h;
    _gcl = new D3D11Client( hDLL );
    if( !oapiRegisterGraphicsClient( _gcl ) ) {
        delete _gcl;
        _gcl = NULL;
    }
}

DLLCLBK void ExitModule(HINSTANCE hDLL){
    oapiWriteLogV("D3D11Client::clbkScaleBlt");
    if( _gcl ) {
        oapiUnregisterGraphicsClient( _gcl );
        _gcl = NULL;
    }
}

void D3D11Client::InitSplashScreen() {
    HBITMAP hbm = LoadBitmap( oapiGetOrbiterInstance(), MAKEINTRESOURCE(273) );
    mSplashScreen = mTexMgr->CreateSurfaceBitmap( hbm );
    mTextSurface = mTexMgr->CreateSurface( 512, 512 );
    mTexMgr->FillSurface( mTextSurface, 0x00 );
    mYPos = 16;
    mTextFont = oapiCreateFont( 14, true, "Consolas", FONT_NORMAL, 0 );

    mSplashScreenQuad[0].pos = D3DXVECTOR2( 0.05f, 0.4f );
    mSplashScreenQuad[1].pos = D3DXVECTOR2( 0.05f, 0.9f );
    mSplashScreenQuad[2].pos = D3DXVECTOR2( 0.6f, 0.9f );
    mSplashScreenQuad[3].pos = D3DXVECTOR2( 0.6f, 0.4f );

    mSplashScreenQuad[0].tex = D3DXVECTOR2( 0.0f, 0.03125f );
    mSplashScreenQuad[1].tex = D3DXVECTOR2( 0.0f, 1.0f );
    mSplashScreenQuad[2].tex = D3DXVECTOR2( 1.0f, 1.0f );
    mSplashScreenQuad[3].tex = D3DXVECTOR2( 1.0f, 0.03125f );

    mbSplashScreenInited = true;
}

void D3D11Client::ProgressString( const char *line, DWORD color ) {
    static const double delta = 0.03125;

    oapiWriteLogV("ProgressString :  %s", line);

    if( !mbSplashScreenInited )    return;

    //draw progress string on splash screen
    switch( color ) {
    case 0:
        color = 0xFFFFFF;    //white
        break;
    case 1:
        color = 0xCFFFCF;    //light-green
        break;
    case 2:
        color = 0xAFCFFF;    //orange
        break;
    case 3:
        color = 0xFF;        //red
        break;
    case 4:
        color = 0xA0FFA0;    //green
        break;
    case 5:
        color = 0xFFA0A0;    //blue
        break;
    }

    //write progress string
	D3D11Pad *Skpad = new D3D11Pad(mConfig, mScene, (SURFHANDLE)mTextSurface, mTexMgr);
    Skpad->SetFont( mTextFont );
    Skpad->SetTextColor( color );
    Skpad->SetTextAlign( oapi::Sketchpad::LEFT, oapi::Sketchpad::BOTTOM );
    Skpad->SetBackgroundMode( oapi::Sketchpad::BK_TRANSPARENT );
    Skpad->Text( 5, mYPos, line, strlen(line) );
    Skpad->SetFont( NULL );
    delete Skpad;

    //draw splash screen
    DWORD w, h;
    mSplashScreen->GetSize( &w, &h );
    mTexMgr->ScaleBlit( NULL, 0, 0, mConfig->cWidth, mConfig->cHeight, mSplashScreen, 0, 0, w, h, 0 );

    static bool shift = false;
    mYPos += 16;
    if( mYPos > 512 ) {
        shift = true;
        mYPos -= 512;
    }

    D3D11_MAPPED_SUBRESOURCE SRes;
    memset( &SRes, 0, sizeof(SRes) );
    iCtx->Map( mTexMgr->VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
    memcpy( SRes.pData, mSplashScreenQuad, 64 );
    iCtx->Unmap( mTexMgr->VB, 0 );

    const UINT VtxStride = 16, VBOffset = 0;
    iCtx->IASetVertexBuffers( 0, 1, &mTexMgr->VB, &VtxStride, &VBOffset );
    iCtx->IASetIndexBuffer( mTexMgr->IB, DXGI_FORMAT_R16_UINT, 0 );
    iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
    iCtx->IASetInputLayout( mTexMgr->IL_Blit );
    iCtx->VSSetConstantBuffers( 0, 1, &mTexMgr->cb_VS );
    iCtx->UpdateSubresource( mTexMgr->cb_VS, 0, NULL, &mTexMgr->Orth, 0, 0 );
    iCtx->VSSetShader( mTexMgr->VS_Blit, NULL, NULL );
    iCtx->PSSetConstantBuffers( 1, 1, &mTexMgr->cb_PS );
    iCtx->PSSetShaderResources( 0, 1, mTextSurface->GetSRV() );
    iCtx->PSSetSamplers( 0, 1, &SS_Point_Wrap );
    iCtx->UpdateSubresource( mTexMgr->cb_PS, 0, NULL, D3DXVECTOR4( 0.3f, 0.3f, 0.3f, 2.0f ), 0, 0 );
    iCtx->PSSetShader( mTexMgr->PS_Blit, NULL, NULL );
    iCtx->OMSetBlendState( BS_InvSrcAlpha, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
    iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );
    iCtx->RSSetState( RS_CullNone_Solid );

    D3D11_VIEWPORT vp;
    vp.Width = (float)mConfig->cWidth;
    vp.Height = (float)mConfig->cHeight;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    vp.MaxDepth = 1;
    vp.MinDepth = 0;

    iCtx->RSSetViewports( 1, &vp );
    iCtx->DrawIndexed( 6, 0, 0 );

    //output result
    SwapChain->Present( 0, 0 );

    if( shift ) {
        mSplashScreenQuad[0].tex.y += (float)delta;
        mSplashScreenQuad[1].tex.y += (float)delta;
        mSplashScreenQuad[2].tex.y += (float)delta;
        mSplashScreenQuad[3].tex.y += (float)delta;
    }

    mTexMgr->FillSurfaceRect( mTextSurface, 0, mYPos-16, 512, 16, 0x0 );
}

void D3D11Client::ExitSplashScreen() {
    delete mTextFont;
    mTexMgr->ReleaseTexture( mTextSurface );
    mTexMgr->ReleaseTexture( mSplashScreen );
    mYPos = 16;
    mbSplashScreenInited = false;
    mTextSurface = mSplashScreen = NULL;
}
